//
//  Helper.h
//  SGD
//
//  Created by Niketan Pansare on 12/19/12.
//  Copyright (c) 2012 Rice University. All rights reserved.
//

#ifndef SGD_Helper_h
#define SGD_Helper_h

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cassert>
#include <fstream>
#include <cstdlib>
#include <cmath>
#include <cfloat>
#include <limits>

#include <sstream>

using namespace std;
#include "Config.h"


class StringHelper {
public:
  // Usage: const vector<string> words = split("So close no matter how far", " ");
  vector<string> split(const string& s, const string& delim, const bool keep_empty = true);
  
  void appendMessageToLogFile(string logFilePath, string message);
  
  void outputTextFileToStdOutput(string fileName, string replaceNewLineByThis);
  
  // Array to be deleted by the caller
  double* getArrayFromSharedDataFile(string sharedDataFilePath, string key, int numDimensions);
  // Returns empty string if unsuccessful
  string getStringFromSharedDataFile(string sharedDataFilePath, string key);
  // Return failedValue if no integer found for the given key
  int getIntegerFromSharedDataFile(string sharedDataFilePath, string key, int failedValue);
  
  string rtrim(string str);
};

// Can jump randomly to a given line number
// TODO: If you are dealing with a small file, I suggest you buffer the entire input file.
class RandomAccessTextFile {
public:
  // i starts with 0
  string getIthLine(int i);
  
  // Parses the line in svm format. Return false if no item found
  // retX should be array of size numDimensions created by the caller and retY is a variable
  void getIthItem(int i, int numDimensions, double* retX, double* retY);
  
  int getNumberOfLinesInTheFile();
  
  RandomAccessTextFile(string inputFileName);
  ~RandomAccessTextFile();
private:
  vector<long> positionNumbers;
  ifstream fin;
};

// This doesnot serve as arbitrary precision class. For that use GMP.
// Use this class when input and output fall into precision of double, but the intermediate calculation needs log-space calculation. For example: dealing with underflows/overflows while multiplying probabilities.
// Doesnot work well if you are dealing with negative numbers.
class LightWeightBigDouble {
public:
  // Constructors:
  // Eg: LightWeightBigDouble t = 9999; LightWeightBigDouble t1 = 100000.0;
  // LightWeightBigDouble t3; t3 = 20; double v = (double) t1;
  LightWeightBigDouble(bool isInitialValInLogSpace, double initialValue) { init(isInitialValInLogSpace, initialValue); }
  LightWeightBigDouble() { init(false, 0); }
  
  // Rule of 3:
  // If your class needs either a copy constructor, an assignment operator, or a destructor, then it is likely to need all three of them. But since I am not using pointers, it's not much of concern here.
  LightWeightBigDouble(const LightWeightBigDouble& copyFrom) { logVal = copyFrom.logVal; }
  LightWeightBigDouble& operator= (const LightWeightBigDouble& copyFrom);
  ~LightWeightBigDouble() {}
  
  LightWeightBigDouble(const double& copyFrom) { init(false, copyFrom); }
  LightWeightBigDouble(const int& copyFrom) { init(false, (double)copyFrom); }
  LightWeightBigDouble& operator= (const double& copyFrom) { init(false, copyFrom); return *this; }
  LightWeightBigDouble& operator= (const int& copyFrom) { init(false, (double)copyFrom); return *this; }
  
  // Arithmetic operators
  // Binary: try 10+myObj for utility of friend function
  friend LightWeightBigDouble operator+(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend LightWeightBigDouble operator-(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend LightWeightBigDouble operator*(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend LightWeightBigDouble operator/(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  // Unary
  friend LightWeightBigDouble operator++(const LightWeightBigDouble& me); // Prefix: ++me
  friend LightWeightBigDouble operator++(const LightWeightBigDouble& me, int ignoreMe); // Suffix: me++
  friend LightWeightBigDouble operator--(const LightWeightBigDouble& me); // Prefix: --me
  friend LightWeightBigDouble operator--(const LightWeightBigDouble& me, int ignoreMe); // Suffix: me--
  
  // Comparison operators
  friend bool operator==(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend bool operator!=(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend bool operator< (const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend bool operator> (const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend bool operator<=(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  friend bool operator>=(const LightWeightBigDouble& lhs, const LightWeightBigDouble& rhs);
  
  // Compound assignment operators
  LightWeightBigDouble& operator+=(const LightWeightBigDouble& rhs);
  LightWeightBigDouble& operator-=(const LightWeightBigDouble& rhs);
  LightWeightBigDouble& operator*=(const LightWeightBigDouble& rhs);
  LightWeightBigDouble& operator/=(const LightWeightBigDouble& rhs);
  
  operator double() { return exp(logVal); }
  double getValue() { return exp(logVal); } // Use only at the end while printing
  void setValue(double value);
  
  friend std::ostream& operator<<(std::ostream &os, const LightWeightBigDouble &me);
  friend std::istream& operator>>(std::istream &is, LightWeightBigDouble &me);

  // Didnot implement following operators:
  // Modulus (%), logical (!, &&, ||), bitwise (~, &, |, ^, <<, >>), pointer operator ([], *, &, ->, ., ), comma, function call, allocate/deallocate operator.
  
private:
  double logVal;
  
  // Internal function to compare in real space
  static bool IsEqual(double a, double b);
  
  static double log_add(double a, double b);
  static double log_subtract(double a, double b);
  
  void init(bool isInitialValInLogSpace, double initialValue);
};


bool IsFiniteNumber(double x);

#endif
